package com.baidu.disconf.client;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.baidu.disconf.client.config.ConfigMgr;
import com.baidu.disconf.client.config.DisClientConfig;
import com.baidu.disconf.client.core.DisconfCoreFactory;
import com.baidu.disconf.client.core.DisconfCoreMgr;
import com.baidu.disconf.client.scan.ScanFactory;
import com.baidu.disconf.client.scan.ScanMgr;
import com.baidu.disconf.client.store.DisconfStoreProcessorFactory;
import com.baidu.disconf.client.support.registry.Registry;
import com.baidu.disconf.client.support.registry.RegistryFactory;

/**
 * Disconf Client 鎬诲叆鍙�
 *
 * @author liaoqiqi
 * @version 2014-5-23
 */
public class DisconfMgr implements ApplicationContextAware{

	protected static final Logger LOGGER = LoggerFactory.getLogger(DisconfMgr.class);

	// 鏈疄渚嬩笉鑳藉垵濮嬪寲涓ゆ
	private boolean isFirstInit = false;
	private boolean isSecondInit = false;

	// application context
	private ApplicationContext applicationContext;

	// 鏍稿績澶勭悊鍣�
	private DisconfCoreMgr disconfCoreMgr = null;

	// scan mgr
	private ScanMgr scanMgr = null;

	protected final static ConcurrentHashMap<ClassLoader,DisconfMgr> INSTANCES = new ConcurrentHashMap<>();

	public static DisconfMgr getInstance() {
		ClassLoader key = Thread.currentThread().getContextClassLoader();
		if(INSTANCES.containsKey(key))
			return INSTANCES.get(key);
		DisconfMgr value = new DisconfMgr();
		INSTANCES.put(key, value);
		return value;
	}

	public static boolean release(ClassLoader key){
		return INSTANCES.remove(key) != null;
	}

	private DisconfMgr() {

	}

	/**
	 * 鎬诲叆鍙�
	 */
	public synchronized void start(List<String> scanPackageList) {

		firstScan(scanPackageList);

		secondScan();
	}

	/**
	 * 绗竴娆℃壂鎻忥紝闈欐�佹壂鎻� for annotation config
	 */
	protected synchronized void firstScan(List<String> scanPackageList) {

		// 璇ュ嚱鏁颁笉鑳借皟鐢ㄤ袱娆�
		if (isFirstInit) {
			LOGGER.info("DisConfMgr has been init, ignore........");
			return;
		}

		try {

			// 瀵煎叆閰嶇疆
			ConfigMgr.init();

			LOGGER.info("******************************* DISCONF START FIRST SCAN *******************************");

			// registry
			Registry registry = RegistryFactory.getSpringRegistry(applicationContext);

			// 鎵弿鍣�
			scanMgr = ScanFactory.getScanMgr(registry);

			// 绗竴娆℃壂鎻忓苟鍏ュ簱
			scanMgr.firstScan(scanPackageList);

			// 鑾峰彇鏁版嵁/娉ㄥ叆/Watch
			disconfCoreMgr = DisconfCoreFactory.getDisconfCoreMgr(registry);
			disconfCoreMgr.process();

			//
			isFirstInit = true;

			LOGGER.info("******************************* DISCONF END FIRST SCAN *******************************");

		} catch (Exception e) {

			LOGGER.error(e.toString(), e);
		}
	}

	/**
	 * 绗簩娆℃壂鎻�, 鍔ㄦ�佹壂鎻�, for annotation config
	 */
	protected synchronized void secondScan() {

		// 璇ュ嚱鏁板繀椤荤涓�娆¤繍琛屽悗鎵嶈兘杩愯
		if (!isFirstInit) {
			LOGGER.info("should run First Scan before Second Scan.");
			return;
		}

		// 绗簩娆℃壂鎻忎篃鍙兘鍋氫竴娆�
		if (isSecondInit) {
			LOGGER.info("should not run twice.");
			return;
		}

		LOGGER.info("******************************* DISCONF START SECOND SCAN *******************************");

		try {

			// 鎵弿鍥炶皟鍑芥暟
			if (scanMgr != null) {
				scanMgr.secondScan();
			}

			// 娉ㄥ叆鏁版嵁鑷抽厤缃疄浣撲腑
			// 鑾峰彇鏁版嵁/娉ㄥ叆/Watch
			if (disconfCoreMgr != null) {
				disconfCoreMgr.inject2DisconfInstance();
			}

		} catch (Exception e) {
			LOGGER.error(e.toString(), e);
		}

		isSecondInit = true;

		//
		// 涓嶅紑鍚� 鍒欎笉瑕佹墦鍗板彉閲弇ap
		//
		if (DisClientConfig.getInstance().ENABLE_DISCONF) {

			//
			String data = DisconfStoreProcessorFactory.getDisconfStoreFileProcessor()
					.confToString();
			if (!StringUtils.isEmpty(data)) {
				LOGGER.info("Conf File Map: {}", data);
			}

			//
			data = DisconfStoreProcessorFactory.getDisconfStoreItemProcessor()
					.confToString();
			if (!StringUtils.isEmpty(data)) {
				LOGGER.info("Conf Item Map: {}", data);
			}
		}
		LOGGER.info("******************************* DISCONF END *******************************");
	}

	/**
	 * reloadable config file scan, for xml config
	 */
	public synchronized void reloadableScan(String fileName) {

		if (!isFirstInit) {
			return;
		}

		if (DisClientConfig.getInstance().ENABLE_DISCONF) {
			try {

				if (!DisClientConfig.getInstance().getIgnoreDisconfKeySet().contains(fileName)) {

					if (scanMgr != null) {
						scanMgr.reloadableScan(fileName);
					}

					if (disconfCoreMgr != null) {
						disconfCoreMgr.processFile(fileName);
					}
					LOGGER.debug("disconf reloadable file: {}", fileName);
				}

			} catch (Exception e) {

				LOGGER.error(e.toString(), e);
			}
		}
	}

	/**
	 * @Description: 鎬诲叧闂�
	 */
	public synchronized void close() {

		try {

			// disconfCoreMgr
			LOGGER.info("******************************* DISCONF CLOSE *******************************");
			if (disconfCoreMgr != null) {
				disconfCoreMgr.release();
			}

			// close, 蹇呴』灏嗗叾璁剧疆涓篎alse,浠ヤ究閲嶆柊鏇存柊
			isFirstInit = false;
			isSecondInit = false;

		} catch (Exception e) {

			LOGGER.error("DisConfMgr close Failed.", e);
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
}
